﻿// $Id: ActivityController.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Definition of \c Salamanca.DataActivities.ActivityController.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Salamanca.DataRules;

namespace Salamanca.DataActivities
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Arguments for activity exit event.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public class ActivityExitedEventArgs:
        EventArgs
    {

        /// <summary>Constructor.</summary>
        public ActivityExitedEventArgs(IActivityData data, bool hasCompleted)
        {
            _Data=data;
            _HasCompleted=hasCompleted;
        }

        /// <summary>Gets the process data.</summary>
        public IActivityData Data
        {
            get
            {
                return _Data;
            }
        }

        /// <summary>Indicates wheteher the activity has completed itself.</summary>
        public bool HasCompleted
        {
            get
            {
                return _HasCompleted;
            }
        }

        /// <summary>The process data.</summary>
        private IActivityData _Data;
        /// <summary>Indicates wheteher the activity has completed itself.</summary>
        private bool _HasCompleted;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface implemented by an activity controller.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IActivityController
    {
        /// <summary>Executes the activity.</summary>
        ActivityStateResult Execute();

        /// <summary>Executes the activity.</summary>
        ActivityStateResult Execute(ActivityStateResult result);

        /// <summary>Marks the current activity state for saving.</summary>
        void MarkupState();

        /// <summary>Gets the current activity state.</summary>
        IActivityState CurrentState
        {
            get;
        }
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>An activity controller implementation.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class ActivityController:
        IActivityController
    {

        /// <summary>Constructor.</summary>
        /// <param name="initialState">The activity initial state.</param>
        public ActivityController(IActivityInitialState initialState)
        {
            _InitialState=initialState;
            _InitialData=initialState.Data.CreateBackup();

            _CurrentState=initialState;
        }

        /// <summary>Executes the activity.</summary>
        public ActivityStateResult Execute()
        {
            ActivityStateResult ret=ActivityStateResult.Stop;
            do
            {
                ret=DoExecute();
            } while (ActivityResult(ret));
            return ret;
        }

        /// <summary>Executes the activity.</summary>
        public ActivityStateResult Execute(ActivityStateResult result)
        {
            if (ActivityResult(result))
                return Execute();
            else
                return result;
        }

        /// <summary>Processes the current state.</summary>
        private ActivityStateResult DoExecute()
        {
            if (_HasCompleted)
                throw new InvalidOperationException(Resources.SR.AlreadyCompletedActivity);

            _CurrentState.Initialize(this);
            return _CurrentState.Handle(this);
        }

        /// <summary>Processes the activity according to the specified activity state result.</summary>
        /// <param name="result">The activity state result.</param>
        /// <returns><c>true</c> if the controller must go on processing the activity ; <c>false</c> if it must pause.</returns>
        private bool ActivityResult(ActivityStateResult result)
        {
            if (_HasCompleted)
                throw new InvalidOperationException(Resources.SR.AlreadyCompletedActivity);

            bool ret=false;

            switch (result)
            {
            case ActivityStateResult.Next:
                Next();
                ret=true;
                break;
            case ActivityStateResult.Previous:
                Previous();
                ret=true;
                break;
            case ActivityStateResult.Cancel:
                Cancel();
                OnExited(new ActivityExitedEventArgs(_CurrentState.Data, _HasCompleted));
                break;
            case ActivityStateResult.End:
                _HasCompleted=true;
                OnExited(new ActivityExitedEventArgs(_CurrentState.Data, _HasCompleted));
                break;
            case ActivityStateResult.Stop:
            default:
                return ret;
            }

            _MarkupStateMemento=null;
            return ret;
        }

        /// <summary>Raises the <see cref="Exited" /> event.</summary>
        private void OnExited(ActivityExitedEventArgs e)
        {
            if (Exited!=null)
                Exited(this, e);
        }

        /// <summary>Cancels the activity.</summary>
        private void Cancel()
        {
            // Restore process and data in their initial state
            _StateMementos.Clear();

            _InitialState.Data.RestoreBackup(_InitialData);

            _CurrentState.Terminate(this);
            _CurrentState=_InitialState;
        }

        /// <summary>Ends the activity.</summary>
        private void End()
        {
            _CurrentState.Terminate(this);
        }

        /// <summary>Steps to the next activity state.</summary>
        private void Next()
        {
            if (!_CurrentState.Validate())
                throw new BrokenRulesException(_CurrentState.GetBrokenRules(), _CurrentState);

            if (_MarkupStateMemento!=null)
                _StateMementos.Push(_MarkupStateMemento);

            _CurrentState.Terminate(this);
            _CurrentState=_CurrentState.NextState;
            _CurrentState.Initialize(this);
        }

        /// <summary>Steps back to the previous activity state.</summary>
        private void Previous()
        {
            IActivityStateMemento m=_StateMementos.Pop();

            IActivityState nextState=m.State;
            nextState.Data.RestoreBackup(m.DataBackup);

            _CurrentState.Terminate(this);
            _CurrentState=nextState;
            _CurrentState.Initialize(this);
        }

        /// <summary>Marks the current activity state for saving.</summary>
        public void MarkupState()
        {
            _MarkupStateMemento=((IActivityStateMementoOriginator)_CurrentState).CreateMemento();
        }

        /// <summary>Indicates whether there is a previous state to step back to or not.</summary>
        public bool CanPrevious
        {
            get
            {
                return _StateMementos.Count>0;
            }
        }

        /// <summary>The current activity state.</summary>
        public IActivityState CurrentState
        {
            get
            {
                return _CurrentState;
            }
        }

        /// <summary>Indicates whether the activity has completed itself or not.</summary>
        public bool HasCompleted
        {
            get
            {
                return _HasCompleted;
            }
        }

        /// <summary>Raised when the activity exits.</summary>
        public event EventHandler<ActivityExitedEventArgs> Exited;

        /// <summary>Indicates whether the activity has completed itself or not.</summary>
        private bool _HasCompleted;
        /// <summary>The current process state.</summary>
        private IActivityState _CurrentState;
        /// <summary>The initial process state.</summary>
        private IActivityInitialState _InitialState;
        private object _InitialData;
        /// <summary>The stack of mementos.</summary>
        private Stack<IActivityStateMemento> _StateMementos=new Stack<IActivityStateMemento>(1);
        /// <summary>The last memento <see cref="MarkupState()">marked</see> to be saved.</summary>
        private IActivityStateMemento _MarkupStateMemento;
    }

}
